<?php
    /*

     Version: MPL 1.1

     Software distributed under the License is distributed on an "AS IS"
     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
     License for the specific language governing rights and limitations
     under the License.

     The Original Code is KASSIOP Framework.

     The Initial Developer of the Original Code is SURIAN Nicolas (aka NairuS) <me@nairus.fr>.
     Portions created by the Initial Developer are Copyright (C) 2010
     The Initial Developer. All Rights Reserved.

     Contributor(s):

     Alternatively, the contents of this file may be used under the terms
     of the MPL license Version 1.1 (the  "MPL" License"), in which case the
     provisions of Version: MPL 1.1 License are applicable instead of those
     above.  If you wish to allow use of your version of this file only
     under the terms of the MPL License and not to allow others to use
     your version of this file under the MPL, indicate your decision by
     deleting  the provisions above and replace  them with the notice and
     other provisions required by the MPL License.  If you do not delete
     the provisions above, a recipient may use your version of this file
     under either the MPL License.

     The contents of this file are subject to the Mozilla Public License
     Version 1.1 (the "License"); you may not use this file except in
     compliance with the License. You may obtain a copy of the License at
     http://www.mozilla.org/MPL/

    */


    require_once 'PHPUnit/Framework.php';
    require_once dirname( __FILE__ ) . '/../../../../kassiop/core/Autoload.php' ;
    Autoload::register() ;

    /**
     * The unit tests of the Model class.
     *
     * @package    unittests.kassiop.core
     * @subpackage models
     *
     * @author NairuS
     * @version 1.2 $Rev: 90 $ $Date: 2011-01-16 09:23:26 +0000 (Sun, 16 Jan 2011) $ $Author: nicolas.surian $
     */
    class ModelTest extends PHPUnit_Framework_TestCase
    {
        /**
         * Defines the model to test.
         *
         * @var ModelClass
         */
        protected $model ;

        /**
         * The init values for the value object to test.
         *
         * @var array
         */
        protected $init ;

        protected function setUp()
        {
            $this->init = array
            (
                ModelClass::END_AT_FIELD   => '31-12-2010 23:59:59',
                ModelClass::ID_FIELD       => 1,
                ModelClass::IS_VALID_FIELD => true,
                ModelClass::MESSAGE_FIELD  => "My message",
                ModelClass::EXCLUDE_FIELD  => "Exclude field",
                ModelClass::ROLE_FIELD     => 'admin',
                ModelClass::SLUG_FIELD     => 'home',
                ModelClass::START_AT_FIELD => '01-11-2010 00:00:00'
            ) ;
            $this->model = new ModelClass( new TestVO( $this->init ) ) ;
        }

        protected function tearDown()
        {
            unset( $this->init );
            unset( $this->model );
        }

        public function testAddSchemaValitator()
        {
            $this->model->addSchemaValidator(new FieldsCompareValidator('left', FieldsCompareValidator::EQUAL, 'right'));
            $validators = $this->model->getSchemaValidators();

            $this->assertTrue( is_array( $validators ), '1.1 The schemaValidators must be an array' );
            $this->assertTrue( count( $validators ) == 2, '1.2 There must have 1 validator' );
            $this->assertTrue( $validators[1] instanceof FieldsCompareValidator, '1.3 The validator must be an instance of FieldsCompareValidator');
            $validator = $validators[1];
            $validator instanceof FieldsCompareValidator;
            $this->assertEquals( 'left' , $validator->getOption(FieldsCompareValidator::$leftFieldKey ), '1.4 The "left_field" option must be well setted' );
            $this->assertEquals( '=='   , $validator->getOption(FieldsCompareValidator::$operatorKey  ), '1.5 The "operator" option must be well setted' );
            $this->assertEquals( 'right', $validator->getOption(FieldsCompareValidator::$rightFieldKey), '1.6 The "right_field" option must be well setted' );

            try
            {
                $this->model->addSchemaValidator(new StringValidator());
            }
            catch( Exception $e )
            {
                $this->assertTrue($e instanceof Exception, '2.1 The exception must be well setted');
            }
        }

        public function testBeforeValidate()
        {
            $this->assertEquals( 6, $this->model->getValidator(ModelClass::MESSAGE_FIELD)->getOption(StringValidator::$minLengthKey), '1. The validator option must be well setted.');
            $this->model->validate();
            $this->assertEquals( 8, $this->model->getValidator(ModelClass::MESSAGE_FIELD)->getOption(StringValidator::$minLengthKey), '2. The validator option must be changed by the before validate method.');
        }

        public function testConstants()
        {
            $this->assertEquals( ModelClass::END_AT_FIELD   , 'endAt'  , 'The constant property must be well setted.' ) ;
            $this->assertEquals( ModelClass::IS_VALID_FIELD , 'isValid', 'The constant property must be well setted.' ) ;
            $this->assertEquals( ModelClass::MESSAGE_FIELD  , 'message', 'The constant property must be well setted.' ) ;
            $this->assertEquals( ModelClass::EXCLUDE_FIELD  , 'exclude', 'The constant property must be well setted.' ) ;
            $this->assertEquals( ModelClass::ROLE_FIELD     , 'role'   , 'The constant property must be well setted.' ) ;
            $this->assertEquals( ModelClass::SLUG_FIELD     , 'slug'   , 'The constant property must be well setted.' ) ;
            $this->assertEquals( ModelClass::START_AT_FIELD , 'startAt', 'The constant property must be well setted.' ) ;
        }

        public function testGetSchemaValidators()
        {
            $this->assertTrue( count($this->model->getSchemaValidators()) == 1, 'There must have 1 schema validator' );
        }

        public function testGetExcludeField()
        {
            $this->assertEquals( 2, count( $this->model->getExcludeFields() ), 'The number of exclude fields must be 2' ) ;
        }

        public function testIsNew()
        {
            $this->assertFalse( $this->model->isNew(), 'The ValueObject must not be a new one.');
        }

        public function testSetExcludeField()
        {
            $this->model->setExcludeFields( array( Model::ID_FIELD ) );
            $this->assertEquals( 3, count( $this->model->getExcludeFields() ), 'The number of exclude fields must be 3' ) ;
        }

        public function testGetValidator()
        {
            $this->assertTrue( $this->model->getValidator( ModelClass::MESSAGE_FIELD ) instanceof StringValidator  , 'The validator of "' . ModelClass::MESSAGE_FIELD . '" field must be an instance of "StringValidator"' );
            $this->assertTrue( $this->model->getValidator( ModelClass::IS_VALID_FIELD ) instanceof BooleanValidator, 'The validator of "' . ModelClass::IS_VALID_FIELD . '" field must be an instance of "BooleanValidator"' );
            $this->assertTrue( $this->model->getValidator( ModelClass::SLUG_FIELD ) instanceof UniqueValidator     , 'The validator of "' . ModelClass::SLUG_FIELD . '" field must be an instance of "UniqueValidator"' );
        }

        public function testGetVO()
        {
            $this->assertTrue( $this->model->getVO() instanceof TestVO, 'The model\'s vo must be an instance of TestVO.');
            $this->assertEquals( true, $this->model->getVO()->isValid, 'The isValid field must be well setted.');
        }

        public function testSetOption()
        {
            $this->assertEquals( $this->model->getValidator( ModelClass::MESSAGE_FIELD )->getOption( StringValidator::$trimKey ), true, '1. The option must be well setted' );
            $messageValidator = $this->model->getValidator( ModelClass::MESSAGE_FIELD );
            $this->model->setOption( StringValidator::$trimKey, false, $messageValidator );
            $this->assertEquals( $this->model->getValidator( ModelClass::MESSAGE_FIELD )->getOption( StringValidator::$trimKey ), false, '2. The option must be changed' );
        }

        public function testSetSchemaValitators()
        {
            $this->model->setSchemaValidators( array(
                new FieldsCompareValidator('left', FieldsCompareValidator::GREATER_THAN, 'right'),
                new FieldsCompareValidator('an_other_left', FieldsCompareValidator::IDENTICAL, 'an_other_right'),
                new FieldsCompareValidator('the_last_left', FieldsCompareValidator::NOT_IDENTICAL, 'the_last_right')
            ) );

            $validators =  $this->model->getSchemaValidators();
            $this->assertTrue( count( $validators ) == 4, '1. There must have 4 schema validator' );

            // get all validators.
            $validator  = $validators[0];
            $validator1 = $validators[1];
            $validator2 = $validators[2];
            $validator3 = $validators[3];
            $validator instanceof FieldsCompareValidator;
            $validator1 instanceof FieldsCompareValidator;
            $validator2 instanceof FieldsCompareValidator;
            $validator3 instanceof FieldsCompareValidator;

            $this->assertEquals( 'startAt' , $validator->getOption(FieldsCompareValidator::$leftFieldKey ), '2.1 The "left_field" option must be well setted' );
            $this->assertEquals( '<='      , $validator->getOption(FieldsCompareValidator::$operatorKey  ), '2.2 The "operator" option must be well setted' );
            $this->assertEquals( 'endAt'   , $validator->getOption(FieldsCompareValidator::$rightFieldKey), '2.3 The "right_field" option must be well setted' );

            $this->assertEquals( 'left' , $validator1->getOption(FieldsCompareValidator::$leftFieldKey ), '3.1 The "left_field" option must be well setted' );
            $this->assertEquals( '>'    , $validator1->getOption(FieldsCompareValidator::$operatorKey  ), '3.2 The "operator" option must be well setted' );
            $this->assertEquals( 'right', $validator1->getOption(FieldsCompareValidator::$rightFieldKey), '3.3 The "right_field" option must be well setted' );

            $this->assertEquals( 'an_other_left' , $validator2->getOption(FieldsCompareValidator::$leftFieldKey ), '4.1 The "left_field" option must be well setted' );
            $this->assertEquals( '==='           , $validator2->getOption(FieldsCompareValidator::$operatorKey  ), '4.2 The "operator" option must be well setted' );
            $this->assertEquals( 'an_other_right', $validator2->getOption(FieldsCompareValidator::$rightFieldKey), '4.3 The "right_field" option must be well setted' );

            $this->assertEquals( 'the_last_left' , $validator3->getOption(FieldsCompareValidator::$leftFieldKey ), '5.1 The "left_field" option must be well setted' );
            $this->assertEquals( '!=='           , $validator3->getOption(FieldsCompareValidator::$operatorKey  ), '5.2 The "operator" option must be well setted' );
            $this->assertEquals( 'the_last_right', $validator3->getOption(FieldsCompareValidator::$rightFieldKey), '5.3 The "right_field" option must be well setted' );
        }

        public function testValidate()
        {
            $result = $this->model->validate();
            $this->assertTrue(  $result , 'The model must be validated.' );
            $this->assertFalse( $this->model->hasErrors(), 'The model must have no error.') ;
        }

        public function testValidateNew()
        {
            $this->init = array
            (
                ModelClass::END_AT_FIELD   => '31-12-2010 23:59:59',
                ModelClass::IS_VALID_FIELD => true,
                ModelClass::MESSAGE_FIELD  => 'My message',
                ModelClass::ROLE_FIELD     => 'user',
                ModelClass::SLUG_FIELD     => 'home',
                ModelClass::START_AT_FIELD => '01-11-2010 00:00:00'
            ) ;
            $this->model = new ModelClass( new TestVO( $this->init ) ) ;
            $this->assertTrue(  $this->model->validate() , 'The model must be validated.' );
            $this->assertFalse( $this->model->hasErrors(), 'The model must have no error.') ;
        }

        public function testUnvalidate()
        {
            // init model to unvalidate.
            $this->init = array
            (
                ModelClass::END_AT_FIELD   => '2010-13-31 23:59:59',
                ModelClass::ID_FIELD       => 'not integer',
                ModelClass::IS_VALID_FIELD => 'not boolean',
                ModelClass::MESSAGE_FIELD  => '',
                ModelClass::ROLE_FIELD     => 'admin',
                ModelClass::SLUG_FIELD     => 'home',
                ModelClass::START_AT_FIELD => '2010-13-01 00:00:00'
            ) ;
            $vo = new TestVO( $this->init );
            $this->model->vo = $vo ;

            $this->assertFalse(  $this->model->validate() , '1.1 The model must be unvalidated.' ) ;
            $this->assertTrue(   $this->model->hasErrors(), '1.2 The model must have some errors.') ;
            $this->assertEquals( 6, count( $this->model->getErrors() ), '1.3 The number of errors must be 6.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::END_AT_FIELD ) instanceof ErrorFieldVO, ModelClass::END_AT_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( $this->model->getError( ModelClass::END_AT_FIELD )->field , ModelClass::END_AT_FIELD, 'The error field must be well setted.' ) ;
            $this->assertEquals( $this->model->getError( ModelClass::END_AT_FIELD )->message , '"2010-13-31 23:59:59" does not match the date format (jj/mm/aaaa hh:mm:ss)', 'The error message must be well setted.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::ID_FIELD ) instanceof ErrorFieldVO, ModelClass::ID_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( $this->model->getError( ModelClass::ID_FIELD )->field , ModelClass::ID_FIELD, 'The error field must be well setted.' ) ;
            $this->assertEquals( $this->model->getError( ModelClass::ID_FIELD )->message , '"not integer" is not an integer.', 'The error message must be well setted.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::IS_VALID_FIELD ) instanceof ErrorFieldVO, ModelClass::IS_VALID_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( $this->model->getError( ModelClass::IS_VALID_FIELD )->field , ModelClass::IS_VALID_FIELD, 'The error field must be well setted.' ) ;
            $this->assertEquals( $this->model->getError( ModelClass::IS_VALID_FIELD )->message , '"not boolean" is not a valid boolean value.', 'The error message must be well setted.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::MESSAGE_FIELD ) instanceof ErrorFieldVO, ModelClass::MESSAGE_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( $this->model->getError( ModelClass::MESSAGE_FIELD )->field , ModelClass::MESSAGE_FIELD, 'The error field must be well setted.' ) ;
            $this->assertEquals( $this->model->getError( ModelClass::MESSAGE_FIELD )->message , 'Required.', 'The error message must be well setted.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::SLUG_FIELD ) instanceof ErrorFieldVO, ModelClass::SLUG_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( ModelClass::SLUG_FIELD, $this->model->getError( ModelClass::SLUG_FIELD )->field , 'The error field must be well setted.' ) ;
            $this->assertEquals( 'The value(s) "home, admin" for the field(s) "slug, role" not unique(s).', $this->model->getError( ModelClass::SLUG_FIELD )->message , 'The error message must be well setted.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::START_AT_FIELD ) instanceof ErrorFieldVO, ModelClass::START_AT_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( $this->model->getError( ModelClass::START_AT_FIELD )->field , ModelClass::START_AT_FIELD, 'The error field must be well setted.' ) ;
            $this->assertEquals( $this->model->getError( ModelClass::START_AT_FIELD )->message , '"2010-13-01 00:00:00" does not match the date format (jj/mm/aaaa hh:mm:ss)', 'The error message must be well setted.' ) ;
            $this->tearDown();

            // change the unvalidate model.
            $this->setUp();
            $this->model->getVO()->role = 'not_exist';

            $this->assertFalse(  $this->model->validate() , '2.1 The model must be unvalidated.' ) ;
            $this->assertTrue(   $this->model->hasErrors(), '2.2 The model must have some errors.') ;
            $this->assertEquals( 1, count( $this->model->getErrors() ), '2.3 The number of errors must be 1.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::ROLE_FIELD ) instanceof ErrorFieldVO, ModelClass::ROLE_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( $this->model->getError( ModelClass::ROLE_FIELD )->field , ModelClass::ROLE_FIELD, 'The error field must be well setted.' ) ;
            $this->assertEquals( $this->model->getError( ModelClass::ROLE_FIELD )->message , 'The value(s) "not_exist" of the field "role" not valid.', 'The error message must be well setted.' ) ;
        }

        public function testUnvalidateNew()
        {
            $this->init = array
            (
                ModelClass::END_AT_FIELD   => '31-12-2010 23:59:58',
                ModelClass::IS_VALID_FIELD => 'not boolean',
                ModelClass::MESSAGE_FIELD  => '',
                ModelClass::ROLE_FIELD     => 'user',
                ModelClass::SLUG_FIELD     => 'contact',
                ModelClass::START_AT_FIELD => '31-12-2010 23:59:59',
            ) ;
            $this->model = new ModelClass( new TestVO( $this->init ) ) ;
            $this->assertFalse(  $this->model->validate() , 'The model must be unvalidated.' ) ;
            $this->assertTrue(   $this->model->hasErrors(), 'The model must have some errors.') ;
            $this->assertEquals( 3, count( $this->model->getErrors() ), 'The number of errors must be 3.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::IS_VALID_FIELD ) instanceof ErrorFieldVO, ModelClass::IS_VALID_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( $this->model->getError( ModelClass::IS_VALID_FIELD )->field , ModelClass::IS_VALID_FIELD, 'The error field must be well setted.' ) ;
            $this->assertEquals( $this->model->getError( ModelClass::IS_VALID_FIELD )->message , '"not boolean" is not a valid boolean value.', 'The error message must be well setted.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::MESSAGE_FIELD ) instanceof ErrorFieldVO, ModelClass::MESSAGE_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( $this->model->getError( ModelClass::MESSAGE_FIELD )->field , ModelClass::MESSAGE_FIELD, 'The error field must be well setted.' ) ;
            $this->assertEquals( $this->model->getError( ModelClass::MESSAGE_FIELD )->message , 'Required.', 'The error message must be well setted.' ) ;

            $this->assertTrue(   $this->model->getError( ModelClass::SLUG_FIELD ) instanceof ErrorFieldVO, ModelClass::SLUG_FIELD. ' field : this error must be an instance of ErrorFieldVO.') ;
            $this->assertEquals( ModelClass::SLUG_FIELD, $this->model->getError( ModelClass::SLUG_FIELD )->field , 'The error field must be well setted.' ) ;
            $this->assertEquals( 'The value(s) "contact, user" for the field(s) "slug, role" not unique(s).', $this->model->getError( ModelClass::SLUG_FIELD )->message , 'The error message must be well setted.' ) ;

            $globalErrors = $this->model->getGlobalErrors();
            $this->assertEquals( 1, count( $globalErrors ), 'The number of global errors must be 1.' ) ;
            $this->assertTrue( $globalErrors[0] instanceof ErrorVO, 'The global error must ba an instance of ErrorVO' );
            $gError = $globalErrors[0];
            $gError instanceof ErrorVO;
            $this->assertEquals('"2010-12-31 23:59:59" must be &lt;= to "2010-12-31 23:59:58".', $gError->message, 'The global error message must be well setted.');
        }

        public function testValidatorsProperty()
        {
            $validators = $this->model->getValidators();
            $this->assertEquals( 7, count( $validators ), '1. The number of validators must be 7' ) ;

            $this->assertTrue(   isset($validators[ModelClass::END_AT_FIELD] ), '2.1 The validator must be setted for this property' );
            $this->assertTrue(   $validators[ModelClass::END_AT_FIELD] instanceof DateTimeValidator, '2.2 The validator must be good instancied.' );

            $this->assertTrue(   isset($validators[ModelClass::ID_FIELD] ), '3.1 The validator must be setted for this property' );
            $this->assertTrue(   $validators[ModelClass::ID_FIELD] instanceof IntegerValidator, '3.2 The validator must be good instancied.' );

            $this->assertTrue(   isset($validators[ModelClass::IS_VALID_FIELD] ), '4.1 The validator must be setted for this property' );
            $this->assertTrue(   $validators[ModelClass::IS_VALID_FIELD] instanceof BooleanValidator, '4.2 The validator must be good instancied.' );

            $this->assertTrue(   isset($validators[ModelClass::MESSAGE_FIELD] ), '5.1 The validator must be setted for this property' );
            $this->assertTrue(   $validators[ModelClass::MESSAGE_FIELD] instanceof StringValidator, '5.2 The validator must be good instancied.' );

            $this->assertTrue(   isset($validators[ModelClass::ROLE_FIELD] ), '6.1 The validator must be setted for this property' );
            $this->assertTrue(   $validators[ModelClass::ROLE_FIELD] instanceof ChoiceDatabaseValidator, '6.2 The validator must be good instancied.' );

            $this->assertTrue(   isset($validators[ModelClass::SLUG_FIELD] ), '7.1 The validator must be setted for this property' );
            $this->assertTrue(   $validators[ModelClass::SLUG_FIELD] instanceof UniqueValidator, '7.2 The validator must be good instancied.' );

            $this->assertTrue(   isset($validators[ModelClass::START_AT_FIELD] ), '8.1 The validator must be setted for this property' );
            $this->assertTrue(   $validators[ModelClass::START_AT_FIELD] instanceof DateTimeValidator, '8.2 The validator must be good instancied.' );
        }
    }
?>